﻿/**
 * Peter
 * Created by: Peter Development Team
 *    http://peter.codeplex.com/
 * 
 * GNU General Public License version 2 (GPLv2)
 *    http://peter.codeplex.com/license
 *
 *  This code is provided on an AS IS basis, with no WARRANTIES,
 *  CONDITIONS or GUARANTEES of any kind.
 *
 **/

using System;
using System.IO;
using System.Threading;
using System.Windows.Controls;
using System.Windows.Input;
using ICSharpCode.AvalonEdit;
using ICSharpCode.AvalonEdit.Highlighting;
using Peter.Common.Code;
using Peter.Common.Icons;
using Peter.Common.MainMenu;
using Peter.Logic.AvalonEdit;

namespace Peter.Controls
{
   /// <summary>
   /// Represents a code editor control in Peter.
   /// </summary>
   public class CodeEditor : TextEditor
   {
      private readonly HighlightCurrentWordBackgroundRenderer m_CurrentWordRenderer;
      private readonly HighlightBracesBackgroundRenderer m_MatchRenderer;
      private readonly ColorBackgroundRenderer m_ColorRenderer;
      //private OverloadInsightWindow m_InsightWindow;
      //private CompletionWindow m_CompletionWindow;
      private bool m_IsCustomHighlight;
      private int m_OpenTries;

      /// <summary>
      /// Initializes a new code editor.
      /// </summary>
      public CodeEditor ()
      {
         this.CreateContextMenu ();
         //this.CommandBindings.Add (new CommandBinding (PeterInternalCommands.ShowCodeCompletion, this.OnShowCodeCompletion));
         this.m_CurrentWordRenderer = new HighlightCurrentWordBackgroundRenderer (this);
         this.m_MatchRenderer = new HighlightBracesBackgroundRenderer (this);
         this.m_ColorRenderer = new ColorBackgroundRenderer (this);
         //this.TextArea.TextEntering += this.OnTextEntering;
         //this.TextArea.TextEntered += this.OnTextEntered;
      }

      /// <summary>
      /// Gets the code completion.
      /// </summary>
      public BaseCodeCompletion CodeCompletion { get; set; }

      /// <summary>
      /// Opens the given file.
      /// </summary>
      /// <param name="file">File to open.</param>
      public bool OpenFile (FileInfo file)
      {
         try
         {
            if (this.m_OpenTries == 5) return false; // TODO: Show error message...
            this.Load (file.FullName);
            this.Document.FileName = file.FullName;
            this.SetHighlighting (file);
            this.m_OpenTries = 0;
            /*if (file.Extension == ".js") {
               var analyzer = new ECMAScriptAnalyzer ();
               analyzer.Analyze (file.FullName, true);
            }*/
            return true;
         }
         catch (Exception)
         {
            this.m_OpenTries++;
            Thread.Sleep (500);
            return this.OpenFile(file);
         }
      }

      /// <summary>
      /// Saves the given file.
      /// </summary>
      /// <param name="fileInfo">File to save to.</param>
      public void SaveFile (FileInfo fileInfo)
      {
         this.Save (fileInfo.FullName);
         fileInfo.Refresh ();
         this.SetHighlighting (fileInfo);
         /*if (this.CodeCompletion != null)
            this.CodeCompletion.UpdateFile(fileInfo.FullName);*/
      }

      /// <summary>
      /// Sets the highlighting based on the given file.
      /// </summary>
      /// <param name="file"></param>
      private void SetHighlighting (FileInfo file)
      {
         if (!this.m_IsCustomHighlight)
         {
            // TODO: Pull this out and make it configurable.
            this.SyntaxHighlighting = file.Name.ToLower () == "gemfile"
               ? HighlightingManager.Instance.GetDefinition ("Ruby")
               : HighlightingManager.Instance.GetDefinitionByExtension (file.Extension);
         }

         /*if (this.CodeCompletion == null && peter != null)
         {
            this.CodeCompletion = peter.CodeCompletions.FirstOrDefault (c => c.Supports (file.Extension));
         }*/
      }

      /// <summary>
      /// Sets the highlighting of this editor.
      /// </summary>
      /// <param name="name">Name of highlighting to set.</param>
      public void SetHighlighting (string name)
      {
         this.m_IsCustomHighlight = true;
         this.SyntaxHighlighting = HighlightingManager.Instance.GetDefinition (name);
      }

      /*/// <summary>
      /// Occurs when text is being entered in the editor.
      /// </summary>
      /// <param name="sender">this</param>
      /// <param name="e">TextCompositionEventArgs</param>
      private void OnTextEntering (object sender, TextCompositionEventArgs e)
      {
         if (e.Text.Length > 0 && this.m_CompletionWindow != null)
         {
            if (!char.IsLetterOrDigit (e.Text [0]))
            {
               this.m_CompletionWindow.CompletionList.RequestInsertion (e);
            }
         }
      }

      /// <summary>
      /// Occurs when text is has been entered in the editor.
      /// </summary>
      /// <param name="sender">this</param>
      /// <param name="e">TextCompositionEventArgs</param>
      private async void OnTextEntered (object sender, TextCompositionEventArgs e)
      {
         if (this.CodeCompletion != null)
            await this.CodeCompletion.ShowCompletion (e.Text, this.m_CompletionWindow, this.m_InsightWindow, this);
      }

      /// <summary>
      /// Occurs when the control + space command has been executed.
      /// </summary>
      /// <param name="sender">Issuer of command.</param>
      /// <param name="e">ExecutedRoutedEventArgs</param>
      private async void OnShowCodeCompletion (object sender, ExecutedRoutedEventArgs e)
      {
         if (this.CodeCompletion != null)
            await this.CodeCompletion.ShowCompletion (null, this.m_CompletionWindow, this.m_InsightWindow, this);
      }*/

      /// <summary>
      /// Closes this code editor.
      /// </summary>
      public void Close ()
      {
         //this.TextArea.TextEntering -= this.OnTextEntering;
         //this.TextArea.TextEntered -= this.OnTextEntered;
         this.m_ColorRenderer.Remove ();
         this.m_MatchRenderer.Remove ();
         this.m_CurrentWordRenderer.Remove ();
      }

      /// <summary>
      /// Creates the context menu for the code editor.
      /// </summary>
      private void CreateContextMenu ()
      {
         this.ContextMenu = new ContextMenu
         {
            Items =
            {
               new MenuItem
                  {
                  Header = "Edit",
                  Icon = new IconDisplay { Icon = MainMenuIcon.Editalt },
                  Items =
                  {
                     new MenuItem
                     {
                        Header = "To Uppercase",
                        Command = AvalonEditCommands.ConvertToUppercase,
                        CommandTarget = this.TextArea,
                        Icon = new IconDisplay { Icon = MainMenuIcon.ArrowUp }
                     },
                     new MenuItem
                     {
                        Header = "To Lowercase",
                        Command = AvalonEditCommands.ConvertToLowercase,
                        CommandTarget = this.TextArea,
                        Icon = new IconDisplay { Icon = MainMenuIcon.ArrowDown }
                     },
                     new MenuItem
                     {
                        Header = "To Title Case",
                        Command = AvalonEditCommands.ConvertToTitleCase,
                        CommandTarget = this.TextArea
                     },
                     new MenuItem
                     {
                        Header = "Invert Case",
                        Command = AvalonEditCommands.InvertCase,
                        CommandTarget = this.TextArea
                     },
                     new Separator (),
                     new MenuItem
                     {
                        Header = "Convert Leading Spaces To Tabs",
                        Command = AvalonEditCommands.ConvertLeadingSpacesToTabs,
                        CommandTarget = this.TextArea
                     },
                     new MenuItem
                     {
                        Header = "Convert Spaces To Tabs",
                        Command = AvalonEditCommands.ConvertSpacesToTabs,
                        CommandTarget = this.TextArea
                     },
                     new MenuItem
                     {
                        Header = "Convert Leading Tabs To Spaces",
                        Command = AvalonEditCommands.ConvertLeadingTabsToSpaces,
                        CommandTarget = this.TextArea
                     },
                     new MenuItem
                     {
                        Header = "Convert Tabs To Spaces",
                        Command = AvalonEditCommands.ConvertTabsToSpaces,
                        CommandTarget = this.TextArea
                     },
                     new Separator (),
                     new MenuItem
                     {
                        Header = "Remove Leading Whitespace",
                        Command = AvalonEditCommands.RemoveLeadingWhitespace,
                        CommandTarget = this.TextArea
                     },
                     new MenuItem
                     {
                        Header = "Remove Trailing Whitespace",
                        Command = AvalonEditCommands.RemoveTrailingWhitespace,
                        CommandTarget = this.TextArea
                     }
                  }
               },
               new Separator (),
               new MenuItem
               {
                  Header = ApplicationCommands.Undo.Text,
                  Command = ApplicationCommands.Undo,
                  Icon = new IconDisplay { Icon = MainMenuIcon.Undo }
               },
               new MenuItem
               {
                  Header = ApplicationCommands.Redo.Text,
                  Command = ApplicationCommands.Redo,
                  Icon = new IconDisplay { Icon = MainMenuIcon.Repeat }
               },
               new Separator (),
               new MenuItem
               {
                  Header = ApplicationCommands.Cut.Text,
                  Command = ApplicationCommands.Cut,
                  Icon = new IconDisplay { Icon = MainMenuIcon.CutScissors }
               },
               new MenuItem
               {
                  Header = ApplicationCommands.Copy.Text,
                  Command = ApplicationCommands.Copy,
                  Icon = new IconDisplay { Icon = MainMenuIcon.Copy }
               },
               new MenuItem
               {
                  Header = ApplicationCommands.Paste.Text,
                  Command = ApplicationCommands.Paste,
                  Icon = new IconDisplay { Icon = MainMenuIcon.ClipboardPaste }
               },
               new MenuItem
               {
                  Header = "Delete Line",
                  Command = AvalonEditCommands.DeleteLine,
                  CommandTarget = this.TextArea,
                  Icon = new IconDisplay { Icon = MainMenuIcon.Remove }
               },
               new MenuItem
               {
                  Header = ApplicationCommands.SelectAll.Text,
                  Command = ApplicationCommands.SelectAll,
                  Icon = new IconDisplay { Icon = MainMenuIcon.SelectionRectangleselection }
               }
            }
         };
      }
   }
}
